﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Estructuras.Funciones.Analizar
{
    /// <summary>
    /// Clase que contiene los datos del diccionario de datos.
    /// </summary>
    public class Valor
    {
        /// <summary>
        /// Indica si el dato asociado apunta a un enlace
        /// </summary>
        private bool _esEnlace = false;

        /// <summary>
        /// Obtiene si el dato asociado apunta a un enlace
        /// </summary>
        public bool EsEnlace
        {
            get
            {
                return _esEnlace;
            }
        }

        /// <summary>
        /// Contiene el enlace destino asociado al dato
        /// </summary>
        private string _enlaceA = string.Empty;

        /// <summary>
        /// Obtiene el enlace destino asociado al dato
        /// </summary>
        public string EnlaceA
        {
            get
            {
                return _enlaceA;
            }
        }

        /// <summary>
        /// Tag asociado al dato.
        /// </summary>
        private string _tag = string.Empty;

        /// <summary>
        /// Obtiene el tag asociado al dato
        /// </summary>
        public string Tag
        {
            get
            {
                return _tag;
            }
        }

        /// <summary>
        /// Contiene la asignación a otros tags
        /// </summary>
        private Dictionary<string, string> _otrosTags = null;

        /// <summary>
        /// Obtiene o establece la asignación a otros tags
        /// </summary>
        public Dictionary<string, string> OtrosTags
        {
            get
            {
                if (_otrosTags == null)
                    _otrosTags = new Dictionary<string, string>();
                return _otrosTags;
            }
            set
            {
                _otrosTags = value;
            }
        }

        /// <summary>
        /// Contiene el valor a escribir a la salida.
        /// </summary>
        private string _salidaModificada = string.Empty;

        /// <summary>
        /// Contiene el valor a escribir a la salida
        /// </summary>
        public string SalidaModificada
        {
            get
            {
                return _salidaModificada;
            }
        }

        /// <summary>
        /// Constructor.
        /// Asigna el tipo del valor como enlace.
        /// </summary>
        /// <param name="enlace">Enlace al que apunta el dato</param>
        public Valor(string enlace)
        {
            this._esEnlace = true;
            this._enlaceA = enlace;
        }

        /// <summary>
        /// Constructor.
        /// Asigna el tipo del valor como dato.
        /// </summary>
        /// <param name="tagAsociado">Tag asociado al dato</param>
        /// <param name="salidaModificada">Salida que se escribirá en la salida</param>
        public Valor(string tagAsociado, string salidaModificada)
        {
            this._esEnlace = false;
            this._tag = tagAsociado;
            this._salidaModificada = salidaModificada;
        }

        /// <summary>
        /// Asigna valores a otros tags del dato.
        /// </summary>
        /// <param name="tag">Nuevo tag a incluir</param>
        /// <param name="valor">Valor del tag</param>
        public void AsignarOtrosTags(string tag, string valor)
        {
            try
            {
                this.OtrosTags.Add(tag, valor);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

    /// <summary>
    /// Clase que contiene la máscara a usar para transformar un dato de entrada.
    /// </summary>
    public class Mascara
    {
        /// <summary>
        /// Máscara del dato de salida a obtener.
        /// </summary>
        private string Salida = string.Empty;

        /// <summary>
        /// Tag asociado al dato.
        /// </summary>
        private string _tag = string.Empty;

        /// <summary>
        /// Obtiene el tag asociado al dato
        /// </summary>
        public string Tag
        {
            get
            {
                return _tag;
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="salida">Máscara de salida asociada a la máscara de entrada</param>
        /// <param name="tagAsociado">Tag asociado al dato</param>
        public Mascara(string salida, string tagAsociado)
        {
            this.Salida = salida;
            this._tag = tagAsociado;
        }

        /// <summary>
        /// Obtiene la máscara de un dato.
        /// </summary>
        /// <param name="dato">Dato del que obtener la máscara</param>
        /// <returns>Máscara del dato</returns>
        public static string obtenerMascara(string dato)
        {
            string mascara = string.Empty;
            for (int i = 0; i < dato.Length; i++)
            {
                if (Char.IsNumber(dato, i))
                    mascara = mascara + "N";
                else if (Char.IsLetter(dato, i))
                    mascara = mascara + "A";
                else
                    mascara = mascara + dato[i];
            }
            return mascara;
        }

        /// <summary>
        /// Obtiene un dato transformado en base a máscaras.
        /// </summary>
        /// <param name="entrada">Dato de entrada</param>
        /// <param name="mascaraEntrada">Máscara del dato de entrada</param>
        /// <returns>Dato de salida con la máscara correspondiente</returns>
        public string obtenerDatoSalida(string entrada, string mascaraEntrada)
        {
            if (!string.Empty.Equals(Salida))
                return modificarMascara(entrada, mascaraEntrada, Salida);
            else
                return entrada;
        }

        /// <summary>
        /// Obtiene un dato transformado en base a máscaras.
        /// </summary>
        /// <param name="dato">Dato de entrada</param>
        /// <param name="mascaraEntrada">Máscara del dato de entrada</param>
        /// <param name="mascaraSalida">Máscara del dato de salida deseado</param>
        /// <returns>Dato de salida con la máscara indicada</returns>
        private static string modificarMascara(string dato, string mascaraEntrada, string mascaraSalida)
        {
            string datoSalida = string.Empty;
            Dictionary<char, int> indicesMascara = new Dictionary<char, int>();
            indicesMascara.Add('A', 0);
            indicesMascara.Add('N', 0);
            for (int i = 0; i < mascaraSalida.Length; i++)
            {
                if (!mascaraSalida[i].Equals('A') && !mascaraSalida[i].Equals('N'))
                    datoSalida += mascaraSalida[i];
                else
                {
                    // Busca dato en la máscara de entrada
                    if (indicesMascara[mascaraSalida[i]] < dato.Length)
                    {
                        while (mascaraEntrada.Length > indicesMascara[mascaraSalida[i]]
                            && !mascaraEntrada[indicesMascara[mascaraSalida[i]]].Equals(mascaraSalida[i]))
                            indicesMascara[mascaraSalida[i]]++;
                        if (mascaraEntrada[indicesMascara[mascaraSalida[i]]].Equals(mascaraSalida[i]))
                        {
                            datoSalida += dato[indicesMascara[mascaraSalida[i]]];
                            indicesMascara[mascaraSalida[i]]++;
                        }
                    }
                }
            }
            return datoSalida;
        }
    }

    /// <summary>
    /// Clase que almacena los datos necesarios a la salida por cada entrada en el registro.
    /// </summary>
    internal class ValorSalida
    {
        /// <summary>
        /// Salida a escribir en el registro de salida.
        /// </summary>
        public string Salida = string.Empty;

        /// <summary>
        /// Valor obtenido de la entrada.
        /// </summary>
        public string Valor = string.Empty;

        /// <summary>
        /// Tag asociado al valor de entrada.
        /// </summary>
        public string Tag = string.Empty;

        /// <summary>
        /// Otros tags asociados al valor de entrada.
        /// </summary>
        private Dictionary<string, string> otrosTags = null;

        /// <summary>
        /// Obtiene los tags asociados al valor de entrada.
        /// </summary>
        public Dictionary<string, string> OtrosTags
        {
            get
            {
                if (otrosTags == null)
                    otrosTags = new Dictionary<string, string>();
                return otrosTags;
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="valor">Valor de entrada</param>
        /// <param name="tag">Tag asociado al valor de entrada</param>
        /// <param name="salida">Salida asociada a la entrada</param>
        /// <param name="otrosTags">Otros tags asociados a la entrada</param>
        public ValorSalida(string valor, string tag, string salida, Dictionary<string, string> otrosTags)
        {
            this.Salida = salida;
            this.Valor = valor;
            this.Tag = tag;
            this.otrosTags = otrosTags;
        }
    }
}
